/*read first
this SAmock is used to go simple C test, and it doesn't have added independance 
except standard C libs. To use this, just add the .h file to your test .c file. then you
can write your own tests using the tools included. by the way, if you want to open 
memory check function, please use sa_malloc(size_t size), sa_free(void* p), 
sa_realloc(void* p, size_t size) rather than the origin functions, or you can use macros.
such as:
#define malloc(size) sa_malloc(size)
#define free(p) sa_free(p)
#define realloc(p,size) sa_realloc(p,size)

SAdefine.h is a lib that provide some tools to let your input and output more visible.
you don't need to add it when testing, but when debugging, it can be useful.

*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stddef.h>
#include <setjmp.h>

#define DEBUG 0               // used for author debug, ignore it.

#define SOURCE_FORMAT "f:%s c:%d "
#define LargestType unsigned long long 
#define chtype2largest(c) ((LargestType)((unsigned)c))
#define chptr2largest(ptr) ((LargestType*)(ptr))
#define chptr2ListNode(ptr)  ((ListNode*)(ptr))

// print error msg to stderr, use format of printf
#define print_error(format,content...) fprintf(stderr,format,##content)
/*assert*/
#define assert_true(expression)  _assert_ture(#expression,chtype2largest(expression),__FILE__,__LINE__)
#define assert_int_equal(a,b)  _assert_int_equal(chtype2largest(a),chtype2largest(b),__FILE__,__LINE__)
#define assert_string_equal(a,b) _assert_string_equal((char*)a,(char*)b,__FILE__,__LINE__)
#define assert_memory_equal(a,b,size) _assert_memory_equal((char*)a, (char*)b, size,__FILE__,__LINE__)
#define assert_value_arrange(value,min,max)  _assert_value_arrange(chtype2largest(value),chtype2largest(min),\
                                                                                                    chtype2largest(max),__FILE__,__LINE__)
/*global variable track*/
#define create_whiteList(num,addrs...)   _create_whiteList(num,##addrs)
#define add_global_variable_track(x)    _add_global_variable(#x,&x,__FILE__,__LINE__)
#define check_global_variable_changed()  _check_global_variable_changed(__FILE__,__LINE__)
/*memory leak check*/
#define sa_malloc(size)  _sa_malloc(size,__FILE__,__LINE__)
#define sa_free(p)     _sa_free(p,__FILE__,__LINE__)
#define sa_realloc(p,size)     _sa_realloc(p,size)
/*mock functions*/
#define mock() _mock(__func__,__FILE__,__LINE__)
#define will_return(function,value) _will_return(#function,chtype2largest(value),__FILE__,__LINE__)
#define expect_value(function,parameter,value)  _expect_value(#function,#parameter,chtype2largest(value),__FILE__,__LINE__)
#define expect_string(function,parameter,string)  _expect_string(#function,#parameter,string,__FILE__,__LINE__)
#define check_expect(parameter)   _check_expect(__func__,#parameter,chtype2largest(parameter),__FILE__,__LINE__)
#define check_expect_s(parameter)   _check_expect_s(__func__,#parameter,parameter,__FILE__,__LINE__)

/*structure definition*/
typedef struct listnode{
    void * contents;
    struct listnode * nextnode;
    int8_t if_sublist;
} ListNode;

typedef struct global_variable_info{
    void * ptr;
    LargestType value;
    char name[32];
} GlobalVariableInfo;

typedef struct whiteList{
    void ** whiteListptr;
    int num;
} WhiteList;

typedef struct block{
    void* ptr;
    char* file;
    int line;
} Block;

typedef struct mock_func_node{
    LargestType value;
    int line;
} mockFNode;

// main frame work of a test
typedef void (*runtestFunction)(int8_t Type);

/*run test about*/
typedef struct Unit_Test{
    char * testname;
    runtestFunction testfunction;
    int8_t testmode;
} UnitTest;
#define STREAM_MODE 0
#define DEBUG_MODE 1
#define MULTI_PTHREAD_MODE 2
#define OUTPUT_TO_FILE_MODE 3   

#define unit_test(function,mode)    {#function, function, mode}
#define run_tests(tests) _run_tests(tests,sizeof(tests)/sizeof(tests[0]))

/*
typedef struct mock_string_node{
    char * str;
    int line;
} mockSNode;
*/
//function types that used to find node for certain rule 
// for developers you can add any function in this format:
/*
example:
ListNode* function(ListNode ** preNode, ListNode * headptr, const char* const tag)
{
    ListNode* curptr = headptr;
    ListNode* preptr = NULL;
    while(curptr){
        if(strcmp((struct Name*)(curptr->contents)->tag,tag)){
            if(preNode)
                *preNode = preptr;
            return curptr;
        }
        preptr = curptr;
        curptr = curptr->nextnode;
    }
    print_error(SOURCE_FORMAT"ERROR:fail to find the node");
    if(preNode)
        *preNode = NULL;
    return NULL;
}
*/
/*preNode is the ptr to node before what you find, may be NULL if you find is at head. headptr is the list head*/
/*WARNING: because this is always used when find recursively using stdarg.h, so if you are not skilled with it
perhaps you need to learn it first, or may cause serious problems*/
typedef ListNode* (*findNode_String_Rule)(ListNode ** preNode, ListNode * headptr, const char* const tag) ;
#define STRING_RULE 0
typedef ListNode* (*findNode_Int_Rule)(ListNode ** preNode, ListNode * headptr, const int tag);
#define INT_RULE 1
typedef ListNode* (*findNode_ptr_Rule)(ListNode ** preNode, ListNode * headptr, const void* tag);
#define PTR_RULE 2
typedef ListNode* (*findNode_mem_Rule)(ListNode ** preNode, ListNode * headptr, const void* tag, int size);
#define MEM_RULE 3

//this is one use case for these tools:
ListNode* find_recursively(ListNode ** pre,ListNode * root,int loop_num,...);


/*global variables, most are linklist head*/
static ListNode * global_variable_list_headptr;
static ListNode * mock_function_return_list_headptr;
static ListNode * mock_function_parameter_list_headptr;
static WhiteList wlist;
static ListNode * block_info_list_headptr;

/*assert functions*/
int _assert_ture(const char * const expression, const LargestType result, const char * const file,const int line);
int _assert_int_equal(LargestType a, LargestType b,const char * const file,const int line);
int _assert_string_equal(const char * const a, const char * const b, const char * const file,const int line );
int _assert_memory_equal(const char * const a, const char * const b,const int size, const char * const file,const int line);
int _assert_value_arrange(const LargestType value, const LargestType min, const LargestType max, const char * const file,const int line );

/*list functions*/
ListNode * create_list_node(void* contents, const int8_t if_sublist,const char * const file,const int line);
void add_node_queuehead(ListNode ** headptr, ListNode * node);
void free_list(ListNode ** headptr) ;          // recursively free all room of the headptr, including its sublists
ListNode* find_function_node(ListNode ** preNode, ListNode * headptr, const char* const tag);

/*global value track functions*/
void _create_whiteList(int len,...);
int if_in_whiteList(void * ptr, void* whitelistptr[], int len);
int _check_global_variable_changed(const char * const file,const int line);  // whiteList is the global variables tha won't be check
void update_global_variable();
int _add_global_variable(const char* const name, void* ptr,const char * const file,const int line);
void destroy_global_variable_list();

/*memory leak check functions*/
void* _sa_malloc(size_t size,const char* const file, const int line);
void _sa_free(void* ptr, const char* const file, const int line);
void* _sa_realloc(void* ptr, size_t size, const char* const file, const int line);
void add_ptr_to_track(void* ptr,const char * const file, const int line );
void _check_memory_leak();

/*mock functions*/
void _will_return(const char* const function, const LargestType value, const char* const file, const int line);
LargestType _mock(const char* const function,const char* const file, const line );
void _expect_value(const char * const function, const char* const parameter,const LargestType value, 
                            const char * const file, const int line);
void _expect_string(const char * const function, const char* const parameter,const char* const String, 
                            const char * const file, const int line);
void _check_expect(const char * const function, const char * const parameter,LargestType value,const char * const file, const int line);
ListNode * get_paraptr_node(const char * const function, const char* const parameter, const char * const file, const int line);
void _check_expect_s(const char * const function, const char * const parameter,const char* str,const char * const file, const int line);


/*run test functions*/
void _run_tests(UnitTest tests[],int test_number);

